package com.anji.plus.gaea.security.plus.modules.user.service.impl;

import com.anji.plus.gaea.bean.KeyValue;
import com.anji.plus.gaea.cache.CacheHelper;
import com.anji.plus.gaea.constant.BaseOperationEnum;
import com.anji.plus.gaea.constant.Enabled;
import com.anji.plus.gaea.constant.GaeaConstant;
import com.anji.plus.gaea.constant.GaeaKeyConstant;
import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
import com.anji.plus.gaea.exception.BusinessException;
import com.anji.plus.gaea.exception.BusinessExceptionBuilder;
import com.anji.plus.gaea.holder.UserContentHolder;
import com.anji.plus.gaea.security.GaeaSecurityProperties;
import com.anji.plus.gaea.security.plus.code.RespCommonCode;
import com.anji.plus.gaea.security.plus.modules.org.dao.GaeaOrgMapper;
import com.anji.plus.gaea.security.plus.modules.org.dao.entity.GaeaOrg;
import com.anji.plus.gaea.security.plus.modules.user.controller.param.GaeaUserPasswordParam;
import com.anji.plus.gaea.security.plus.modules.user.controller.param.UserRoleOrgReqParam;
import com.anji.plus.gaea.security.plus.modules.user.dao.GaeaUserMapper;
import com.anji.plus.gaea.security.plus.modules.user.dao.GaeaUserRoleOrgMapper;
import com.anji.plus.gaea.security.plus.modules.user.dao.entity.GaeaUser;
import com.anji.plus.gaea.security.plus.modules.user.dao.entity.GaeaUserRoleOrg;
import com.anji.plus.gaea.security.plus.modules.user.service.GaeaUserService;
import com.anji.plus.gaea.security.utils.MD5Util;
import com.anji.plus.gaea.security.utils.SecurityUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户表(GaeaUser)ServiceImpl
 *
 * @author lr
 * @since 2021-02-02 13:38:12
 */
@Service
public class GaeaUserServiceImpl implements GaeaUserService {

    @Autowired
    private GaeaUserMapper gaeaUserMapper;

    @Autowired
    private GaeaUserRoleOrgMapper gaeaUserRoleOrgMapper;

    @Autowired
    private GaeaOrgMapper gaeaOrgMapper;

    @Autowired
    private CacheHelper cacheHelper;

    @Autowired
    private GaeaSecurityProperties gaeaSecurityProperties;


    @Override
    public GaeaBaseMapper<GaeaUser> getMapper() {
        return gaeaUserMapper;
    }

    @Override
    public GaeaUser getUserByUsername(String username) {
        LambdaQueryWrapper<GaeaUser> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(GaeaUser::getUsername, username);
        return gaeaUserMapper.selectOne(queryWrapper);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveRoleTree(UserRoleOrgReqParam reqParam) {
        String username = reqParam.getUsername().toLowerCase();
        List<String> orgRoleCodes = reqParam.getRoleOrgCodes();
        //该用户已经关联的组织角色
        LambdaQueryWrapper<GaeaUserRoleOrg> userRoleOrgWrapper = Wrappers.lambdaQuery();
        userRoleOrgWrapper.select(GaeaUserRoleOrg::getOrgCode, GaeaUserRoleOrg::getRoleCode)
                .eq(GaeaUserRoleOrg::getUsername, username);
        gaeaUserRoleOrgMapper.delete(userRoleOrgWrapper);

        //删除缓存
        String key = GaeaKeyConstant.USER_ROLE_SET_PREFIX + username;
        cacheHelper.delete(key);

        if (CollectionUtils.isEmpty(orgRoleCodes)) {
            return true;
        }
        List<GaeaUserRoleOrg> list = new ArrayList<>();


        orgRoleCodes.stream()
                .filter(codeStr -> StringUtils.isNotBlank(codeStr) && codeStr.contains(GaeaConstant.REDIS_SPLIT))
                .forEach(codesStr -> {
            GaeaUserRoleOrg userRoleOrg = new GaeaUserRoleOrg();
            String[] info = codesStr.split(GaeaConstant.REDIS_SPLIT);
            userRoleOrg.setOrgCode(info[0]);
            userRoleOrg.setRoleCode(info[1]);
            userRoleOrg.setUsername(username);
            list.add(userRoleOrg);
        });
        gaeaUserRoleOrgMapper.insertBatch(list);

        //刷新用户、机构、角色GaeaKeyConstant.USER_ROLE_SET_PREFIX + : + orgCode => map
        Map<String, String> userOrgRoleMap = list.stream()
                .collect(Collectors.groupingBy(GaeaUserRoleOrg::getOrgCode,
                        Collectors.mapping(GaeaUserRoleOrg::getRoleCode, Collectors.joining(GaeaConstant.SPLIT))));


        cacheHelper.hashSet(key, userOrgRoleMap);
        return true;
    }

    @Override
    public Boolean updatePassword(GaeaUserPasswordParam requestParam) {
        //参数校验
        if (!requestParam.getConfirmPassword().equals(requestParam.getPassword())) {
            //密码和确认密码不一致
            throw BusinessExceptionBuilder.build(RespCommonCode.AUTH_PASSWORD_NOTSAME);
        }
        //新密码不能与老密码一样
        if (StringUtils.equals(requestParam.getOldPassword(), requestParam.getPassword())) {
            throw BusinessExceptionBuilder.build(RespCommonCode.USER_PASSWORD_CONFIG_PASSWORD_CANOT_EQUAL);
        }
        String password = SecurityUtils.getPassword(requestParam.getPassword());
        String oldPassword = SecurityUtils.getPassword(requestParam.getOldPassword());
        String username = UserContentHolder.getContext().getUsername();

        LambdaQueryWrapper<GaeaUser> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(GaeaUser::getId, GaeaUser::getPassword)
                .eq(GaeaUser::getUsername, username);
        GaeaUser gaeaUser = gaeaUserMapper.selectOne(queryWrapper);

        if (SecurityUtils.matches(gaeaUser.getPassword(), oldPassword)) {
            throw BusinessExceptionBuilder.build(RespCommonCode.OLD_PASSWORD_ERROR);
        }
        gaeaUser.setPassword(password);
        int flag = gaeaUserMapper.updateById(gaeaUser);
        //返回结果
        return flag > 0;
    }


    @Override
    public List<KeyValue> getOrgByUsername(String username) {
        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();

        wrapper.eq(GaeaUserRoleOrg::getUsername,username);
        Set<String> orgCodes = gaeaUserRoleOrgMapper.selectList(wrapper).stream()
                .map(GaeaUserRoleOrg::getOrgCode)
                .collect(Collectors.toSet());

        LambdaQueryWrapper<GaeaOrg> orgWrapper = Wrappers.lambdaQuery();
        orgWrapper.in(GaeaOrg::getOrgCode, orgCodes);
        List<GaeaOrg> gaeaOrgs = gaeaOrgMapper.selectList(orgWrapper);
        return gaeaOrgs.stream().map(gaeaOrg -> {
            KeyValue keyValue = new KeyValue();
            keyValue.setText(gaeaOrg.getOrgName());
            keyValue.setId(gaeaOrg.getOrgCode());
            return keyValue;
        }).collect(Collectors.toList());
    }

    @Override
    public List<String> getRoleByUserOrg(String username, String orgCode) {
        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.select(GaeaUserRoleOrg::getRoleCode, GaeaUserRoleOrg::getId)
                .eq(GaeaUserRoleOrg::getUsername, username)
                .and(StringUtils.isNotBlank(orgCode), e -> e.eq(GaeaUserRoleOrg::getOrgCode, orgCode));
        List<GaeaUserRoleOrg> gaeaUserRoleOrgList = gaeaUserRoleOrgMapper.selectList(wrapper);
        return gaeaUserRoleOrgList.stream().map(GaeaUserRoleOrg::getRoleCode).collect(Collectors.toList());
    }

    /**
     * 新增用户，需要设置默认密码
     * @param entity        前端传递的对象
     * @param operationEnum 操作类型
     * @throws BusinessException
     */
    @Override
    public void processBeforeOperation(GaeaUser entity, BaseOperationEnum operationEnum) throws BusinessException {
        switch (operationEnum) {
            case INSERT:
                setDefault(entity);
                break;
            default:
        }
    }

    @Override
    public void processAfterOperation(GaeaUser entity, BaseOperationEnum operationEnum) throws BusinessException {
        String key = GaeaKeyConstant.USER_NICKNAME_KEY;
        String uuid = UserContentHolder.getUuid();
        switch (operationEnum) {
            case INSERT:
            case UPDATE:
                cacheHelper.hashSet(key, entity.getUsername(), entity.getNickname());
                if (Enabled.NO.getValue().equals(entity.getEnabled())) {
                    //清空登录状态
                    cacheHelper.delete(GaeaKeyConstant.USER_LOGIN_TOKEN + entity.getUsername() + GaeaConstant.REDIS_SPLIT + uuid);
                }
                break;
            case DELETE:
                cacheHelper.hashDel(key, entity.getUsername());
                cacheHelper.delete(GaeaKeyConstant.USER_LOGIN_TOKEN + entity.getUsername() + GaeaConstant.REDIS_SPLIT + uuid);
                break;
            default:
        }
    }


    @Override
    public void processBatchAfterOperation(List<GaeaUser> entities, BaseOperationEnum operationEnum) throws BusinessException {
        String uuid = UserContentHolder.getUuid();
        List<String> loginTokenKeys = entities.stream()
                .map(gaeaUser -> GaeaKeyConstant.USER_LOGIN_TOKEN + gaeaUser.getUsername() + GaeaConstant.REDIS_SPLIT + uuid)
                .collect(Collectors.toList());
        switch (operationEnum) {
            case DELETE_BATCH:
                //清空登录状态
                cacheHelper.delete(loginTokenKeys);

                Set<String> usernameSet = entities.stream().map(GaeaUser::getUsername).collect(Collectors.toSet());
                String key = GaeaKeyConstant.USER_NICKNAME_KEY;
                cacheHelper.hashBatchDel(key, usernameSet);
                break;
            default:
        }

    }

    /**
     * 设置默认值
     * @param entity
     */
    private void setDefault(GaeaUser entity) {
        String md5Pwd = MD5Util.encryptBySalt(gaeaSecurityProperties.getDefaultPassword());
        String defaultPwd = SecurityUtils.getPassword(md5Pwd);
        entity.setPassword(defaultPwd);
        if (entity.getMultiLogin() == null) {
            entity.setMultiLogin(Enabled.NO.getValue());
        }
        entity.setAccountNonExpired(Enabled.YES.getValue());
        entity.setCredentialsNonExpired(Enabled.YES.getValue());
        entity.setAccountLocked(Enabled.NO.getValue());
    }

    @Override
    public void refreshCache(List<String> usernameList) {

        LambdaQueryWrapper<GaeaUser> wrapper = Wrappers.lambdaQuery();

        if (!CollectionUtils.isEmpty(usernameList)) {
            wrapper.in(GaeaUser::getUsername, usernameList);
        }

        List<GaeaUser> gaeaUsers = gaeaUserMapper.selectList(wrapper);

        Map<String, String> map = gaeaUsers.stream().collect(Collectors.toMap(GaeaUser::getUsername, GaeaUser::getNickname, (v1, v2) -> v2));

        String key = GaeaKeyConstant.USER_NICKNAME_KEY;
        cacheHelper.hashBatchDel(key, map.keySet());
        cacheHelper.hashSet(key, map);
    }

    @Override
    public List<String> getOrgCodes(String username) {
        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.select(GaeaUserRoleOrg::getOrgCode);
        wrapper.eq(GaeaUserRoleOrg::getUsername, username);
        return gaeaUserRoleOrgMapper.selectList(wrapper).stream()
                .map(GaeaUserRoleOrg::getOrgCode).distinct()
                .collect(Collectors.toList());
    }

    @Override
    public List<String> getOrgRoleMappings(String username) {
        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(GaeaUserRoleOrg::getUsername, username);
        return gaeaUserRoleOrgMapper.selectList(wrapper)
                .stream()
                .map(gaeaUserRoleOrg -> gaeaUserRoleOrg.getOrgCode() + GaeaConstant.REDIS_SPLIT + gaeaUserRoleOrg.getRoleCode())
                .collect(Collectors.toList());
    }

    @Override
    public List<KeyValue> userSelect() {
        Map<String, String> userMap = cacheHelper.hashGet(GaeaKeyConstant.USER_NICKNAME_KEY);

        List<KeyValue> userSelect = userMap.entrySet().stream().map(entry -> {
            KeyValue keyValue = new KeyValue();
            keyValue.setId(entry.getKey());
            keyValue.setText(entry.getValue());
            return keyValue;
        }).collect(Collectors.toList());

        return userSelect;
    }


    @Override
    public void setDefaultPwd(List<String> usernameList) {
        LambdaQueryWrapper<GaeaUser> wrapper = Wrappers.lambdaQuery();
        wrapper.in(GaeaUser::getUsername, usernameList);
        List<GaeaUser> gaeaUsers = list(wrapper);

        String md5Pwd = MD5Util.encryptBySalt(gaeaSecurityProperties.getDefaultPassword());
        String defaultPwd = SecurityUtils.getPassword(md5Pwd);
        Map<String,Object> map = new HashMap<>();
        map.put("password", defaultPwd);
        map.put("account_locked", Enabled.NO.getValue());
        map.put("account_non_expired", Enabled.YES.getValue());
        map.put("credentials_non_expired", Enabled.YES.getValue());
        map.put("password_update_time", new Date());
        gaeaUserMapper.updateFieldsBatch(map, gaeaUsers);
    }

    @Override
    public void unLock(List<String> usernameList) {

        LambdaQueryWrapper<GaeaUser> wrapper = Wrappers.lambdaQuery();
        wrapper.in(GaeaUser::getUsername, usernameList);
        List<GaeaUser> gaeaUsers = list(wrapper);

        Map<String,Object> map = new HashMap<>();
        map.put("account_locked", Enabled.NO.getValue());
        gaeaUserMapper.updateFieldsBatch(map, gaeaUsers);
    }

    @Override
    public Map<String,String> getUserOrgRoleMap(String username) {

        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(GaeaUserRoleOrg::getUsername, username);

        List<GaeaUserRoleOrg> gaeaUserRoleOrgList = gaeaUserRoleOrgMapper.selectList(wrapper);

        return gaeaUserRoleOrgList.stream().collect(Collectors
                .groupingBy(GaeaUserRoleOrg::getOrgCode,
                        Collectors.mapping(GaeaUserRoleOrg::getRoleCode, Collectors.joining(GaeaConstant.SPLIT))));
    }

    @Override
    public List<GaeaUser> getUserByNames(List<String> usernames) {
        LambdaQueryWrapper<GaeaUser> wrapper = Wrappers.lambdaQuery();
        wrapper.select(GaeaUser::getId,GaeaUser::getUsername,GaeaUser::getDeviceId)
                .in(GaeaUser::getUsername,usernames);
        return gaeaUserMapper.selectList(wrapper);
    }


    @Override
    public List<GaeaUser> getUsersByOrgCode(String orgCode) {
        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(GaeaUserRoleOrg::getOrgCode, orgCode);

        List<GaeaUserRoleOrg> gaeaUserRoleOrgs = gaeaUserRoleOrgMapper.selectList(wrapper);

        List<String> usernames = gaeaUserRoleOrgs.stream().map(GaeaUserRoleOrg::getUsername).collect(Collectors.toList());
        return getUserByNames(usernames);
    }
}
